﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;
using System.Linq.Expressions;
using System.Reflection;
using System.Collections;
using System.Collections.ObjectModel;
using System.Diagnostics;

namespace VehicleScale.Models
{
    [Serializable]
    public abstract class ModelBase
    {
        #region Properties use in progress

        /// <summary>
        /// Creating status
        /// </summary>
        [NonSerialized]
        private bool _isNew = false;
        public bool IsNew
        {
            get { return _isNew; }
            set
            {
                if (_isNew != value)
                {
                    OnIsNewChanging(value);
                    _isNew = value;
                    OnIsNewChanged();
                }
            }
        }

        /// <summary>
        /// Editing status
        /// </summary>
        [NonSerialized]
        private bool _isEdit = false;
        public bool IsEdit
        {
            get { return _isEdit; }
            set
            {
                if (_isEdit != value)
                {
                    OnIsEditChanging(value);
                    _isEdit = value;
                    OnIsEditChanged();
                }
            }
        }

        /// <summary>
        /// Deleted status
        /// </summary>
        [NonSerialized]
        private bool _isDelete = false;
        public bool IsDelete
        {
            get { return _isDelete; }
            set
            {
                if (_isDelete != value)
                {
                    OnIsDeleteChanging(value);
                    _isDelete = value;
                    OnIsDeleteChanged();
                }
            }
        }

        /// <summary>
        /// Selected status
        /// </summary>
        [NonSerialized]
        private bool _isChecked = false;
        public bool IsChecked
        {
            get { return _isChecked; }
            set
            {
                if (_isChecked != value)
                {
                    OnIsCheckedChanging(value);
                    _isChecked = value;
                    OnIsCheckedChanged();
                }
            }
        }

        /// <summary>
        /// Selected status
        /// </summary>
        [NonSerialized]
        private bool _isSelected = false;
        public bool IsSelected
        {
            get { return _isSelected; }
            set
            {
                if (_isSelected != value)
                {
                    OnIsSelectedChanging(value);
                    _isSelected = value;
                    OnIsSelectedChanged();
                }
            }
        }

        #endregion

        #region Methods

        #region Extension Methods

        protected virtual void OnIsNewChanging(object value) { }
        protected virtual void OnIsNewChanged() { }

        protected virtual void OnIsEditChanging(object value) { }
        protected virtual void OnIsEditChanged() { }

        protected virtual void OnIsDeleteChanging(object value) { }
        protected virtual void OnIsDeleteChanged() { }

        protected virtual void OnIsCheckedChanging(object value) { }
        protected virtual void OnIsCheckedChanged() { }

        protected virtual void OnIsSelectedChanging(object value) { }
        protected virtual void OnIsSelectedChanged() { }

        #endregion

        #region General change Methods

        protected virtual void OnChanging(object value)
        {

        }

        protected virtual void OnChanged()
        {
            if (!IsEdit) IsEdit = true;
        }

        #endregion

        #region Clone and Copy Methods

        #region ICopyable Memebers
        /// <summary>
        /// Copy to a new object
        /// </summary>
        /// <returns></returns>
        public void CopyTo<T>(T model)
        {
            try
            {
                PropertyInfo[] propertyInfos = this.GetType().GetProperties();
                foreach (PropertyInfo info in propertyInfos)
                {
                    if (info.Name.Contains("Error") || info.Name == "Item" || !info.CanWrite) continue;
                    if (CheckValueType(info.PropertyType))
                    {
                        info.SetValue(model, info.GetValue(this, null), null);
                    }
                    else if (!CheckValueCollection(info.PropertyType))
                    {
                        info.SetValue(model, Activator.CreateInstance(info.PropertyType), null);
                        CopyTo(info.GetValue(this, null), info.GetValue(model, null));
                    }
                }
            }
            catch (Exception ex)
            {
                Debug.WriteLine(ex.Message);
            }
        }

        /// <summary>
        /// Copy to a new object with type T and K is the type of collection child.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <typeparam name="K"></typeparam>
        /// <param name="model"></param>
        public void CopyTo<T, D1>(T model)
        {
            try
            {
                PropertyInfo[] propertyInfos = this.GetType().GetProperties();
                foreach (PropertyInfo info in propertyInfos)
                {
                    if (info.Name.Contains("Error") || info.Name == "Item" || !info.CanWrite) continue;
                    if (CheckValueType(info.PropertyType))
                    {
                        info.SetValue(model, info.GetValue(this, null), null);
                    }
                    else if (!CheckValueCollection(info.PropertyType))
                    {
                        info.SetValue(model, Activator.CreateInstance(info.PropertyType), null);
                        CopyTo(info.GetValue(this, null), info.GetValue(model, null));
                    }
                    else
                    {
                        // Collection Instance
                        ICollection collection = info.GetValue(this, null) as ICollection;
                        if (collection.GetType().GetProperties()[1].PropertyType == typeof(D1))
                        {
                            ICollection<D1> collectionTarget = Activator.CreateInstance(info.PropertyType) as ICollection<D1>;
                            IEnumerable<D1> collectionSource = collection.OfType<D1>();
                            D1 temp;
                            // Copy all items of the collection source to the collection target
                            foreach (var item in collectionSource)
                            {
                                temp = Activator.CreateInstance<D1>();
                                CopyTo(item, temp);
                                collectionTarget.Add(temp);
                            }
                            info.SetValue(model, collectionTarget, null);
                        }

                    }
                }
            }
            catch (Exception ex)
            {
                Debug.WriteLine(ex.Message);
            }
        }

        /// <summary>
        /// Copy to a new object with type T and K, L, M, N are the types of collection children.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <typeparam name="K"></typeparam>
        /// <typeparam name="L"></typeparam>
        /// <typeparam name="M"></typeparam>
        /// <typeparam name="N"></typeparam>
        /// <param name="model"></param>
        public void CopyTo<T, D1, D2, D3, D4>(T model)
        {
            try
            {
                PropertyInfo[] propertyInfos = this.GetType().GetProperties();
                foreach (PropertyInfo info in propertyInfos)
                {
                    if (info.Name.Contains("Error") || info.Name == "Item" || !info.CanWrite) continue;
                    if (CheckValueType(info.PropertyType))
                    {
                        info.SetValue(model, info.GetValue(this, null), null);
                    }
                    else if (!CheckValueCollection(info.PropertyType))
                    {
                        info.SetValue(model, Activator.CreateInstance(info.PropertyType), null);
                        CopyTo(info.GetValue(this, null), info.GetValue(model, null));
                    }
                    else
                    {
                        // Collection Instance
                        ICollection collection = info.GetValue(this, null) as ICollection;
                        if (collection.GetType().GetProperties()[1].PropertyType == typeof(D1))
                        {
                            ICollection<D1> collectionTarget = Activator.CreateInstance(info.PropertyType) as ICollection<D1>;
                            IEnumerable<D1> collectionSource = collection.OfType<D1>();
                            D1 temp;
                            // Copy all items of the collection source to the collection target
                            foreach (var item in collectionSource)
                            {
                                temp = Activator.CreateInstance<D1>();
                                CopyTo(item, temp);
                                collectionTarget.Add(temp);
                            }
                            info.SetValue(model, collectionTarget, null);
                        }
                        else if (collection.GetType().GetProperties()[1].PropertyType == typeof(D2))
                        {
                            ICollection<D2> collectionTarget = Activator.CreateInstance(info.PropertyType) as ICollection<D2>;
                            IEnumerable<D2> collectionSource = collection.OfType<D2>();
                            D2 temp;
                            // Copy all items of the collection source to the collection target
                            foreach (var item in collectionSource)
                            {
                                temp = Activator.CreateInstance<D2>();
                                CopyTo(item, temp);
                                collectionTarget.Add(temp);
                            }
                            info.SetValue(model, collectionTarget, null);
                        }
                        else if (collection.GetType().GetProperties()[1].PropertyType == typeof(D3))
                        {
                            ICollection<D3> collectionTarget = Activator.CreateInstance(info.PropertyType) as ICollection<D3>;
                            IEnumerable<D3> collectionSource = collection.OfType<D3>();
                            D3 temp;
                            // Copy all items of the collection source to the collection target
                            foreach (var item in collectionSource)
                            {
                                temp = Activator.CreateInstance<D3>();
                                CopyTo(item, temp);
                                collectionTarget.Add(temp);
                            }
                            info.SetValue(model, collectionTarget, null);
                        }
                        else if (collection.GetType().GetProperties()[1].PropertyType == typeof(D4))
                        {
                            ICollection<D4> collectionTarget = Activator.CreateInstance(info.PropertyType) as ICollection<D4>;
                            IEnumerable<D4> collectionSource = collection.OfType<D4>();
                            D4 temp;
                            // Copy all items of the collection source to the collection target
                            foreach (var item in collectionSource)
                            {
                                temp = Activator.CreateInstance<D4>();
                                CopyTo(item, temp);
                                collectionTarget.Add(temp);
                            }
                            info.SetValue(model, collectionTarget, null);
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                Debug.WriteLine(ex.Message);
            }
        }

        #endregion

        #region ICloneable Members

        /// <summary>
        /// Clone an object with type T
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <returns></returns>
        public T Clone<T>()
        {
            var model = Activator.CreateInstance<T>();
            CopyTo<T>(model);
            return model;
        }

        /// <summary>
        /// Clone an object with type T and K is the type of collection child.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <typeparam name="K"></typeparam>
        /// <returns></returns>
        public T Clone<T, D1>()
        {
            var model = Activator.CreateInstance<T>();
            CopyTo<T, D1>(model);
            return model;
        }

        /// <summary>
        /// Clone an object with type T and K, L, M, N are the types of collection children.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <typeparam name="K"></typeparam>
        /// <typeparam name="L"></typeparam>
        /// <typeparam name="M"></typeparam>
        /// <typeparam name="N"></typeparam>
        /// <returns></returns>
        public T Clone<T, D1, D2, D3, D4>()
        {
            var model = Activator.CreateInstance<T>();
            CopyTo<T, D1, D2, D3, D4>(model);
            return model;
        }

        private void CopyTo(object model, object model1)
        {
            try
            {
                if (model == null) return;
                PropertyInfo[] propertyInfos = model.GetType().GetProperties();
                foreach (PropertyInfo info in propertyInfos)
                {
                    if (info.Name.Contains("Error") || info.Name == "Item" || !info.CanWrite) continue;

                    if (CheckValueType(info.PropertyType))
                    {
                        info.SetValue(model1, info.GetValue(model, null), null);
                    }
                }
            }
            catch (Exception ex)
            {
                Debug.WriteLine(ex.Message);
            }
        }
        #endregion

        #region CheckValue
        private bool CheckValueType(Type type)
        {
            return (type.IsValueType || type.FullName == "System.String" || type.Namespace == "System");
        }

        private bool CheckValueCollection(Type type)
        {
            if (type.IsGenericType || type.Namespace == "System.Colletions" || type.Namespace == "System.Array") return true;
            if (type.BaseType != null)
                return type.BaseType.IsGenericType;
            return false;
        }
        #endregion

        #endregion

        #endregion
    }
}
